// system.cc 
//	Nachos initialization and cleanup routines.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"

// This defines *all* of the global data structures used by Nachos.
// These are all initialized and de-allocated by this file.

Thread *currentThread;			// the thread we are running now
Thread *threadToBeDestroyed;  		// the thread that just finished
Scheduler *scheduler;			// the ready list
Interrupt *interrupt;			// interrupt status
Statistics *stats;			// performance metrics
Timer *timer;				// the hardware timer device,
					// for invoking context switches

#ifdef FILESYS_NEEDED
FileSystem  *fileSystem;
#endif

#ifdef FILESYS
SynchDisk   *synchDisk;
#endif

#ifdef USER_PROGRAM	// requires either FILESYS or FILESYS_STUB
Machine *machine;	// user program memory and registers

//---------------------------------------
//	Added in project2
//  Declearation
//  by OceanMaster
//---------------------------------------
Lock * deleteLock;
int ThreadIndex;

//To Manager Process
Lock * ProcessTableLock;
KernelProcess * * ProcessTable;
int nextProcessId;					
int processNum;


//To Manage Memory Pags allocation
BitMap * MemoryMap;
Lock * MemoryLock;

//To Manage Lock Allocation
KernelLock * * LockTable;
int nextLockIndex;
Lock * LockTableLock;

//To Manage Condition Allocation
KernelCondition * * ConditionTable;
int nextConditionIndex;
Lock * ConditionTableLock;

//---------------------------------------
//	Utility Functions
//---------------------------------------
//Description: Validate input index of Lock
//Assumption: holding LockTableLock
bool ValidateLockIndex(int index)
{
	if(index<0 || index>=LockTableSize)					//Invalid Index
	{
		printf("ERROR: Lock index [%d] out of bounds \n", index);
		return false;
	}

	KernelLock * klock = LockTable[index];

	if(klock == NULL)									//Lock doesn't exist
	{
		printf("ERROR: requested Lock [%d] doesn't exist \n", index);
		return false;
	}

	if(klock->processId != currentThread->processId )	//Lock doesn't belong to current process
	{
		printf("ERROR: requested Lock [%d] doesn't belong to current process \n", index);
		return false;
	}

	if(klock->isDeleted)								//Lock has been deleted
	{
		printf("ERROR: requested Lock [%d] has been deleted \n", index);
		return false;
	}

	return true;										//Pass Validation
}

//Description: Clean Lock Table, remove deleted lock and update nextLockIndex
//Assumption: holding LockTableLock
void CleanLockTable()
{
	for(int i=LockTableSize - 1 ; i>=0 ; i--)
	{
		KernelLock * klock = LockTable[i];

		if(klock == NULL)
		{
			nextLockIndex = i;
			continue;
		}
		
		if(klock->isDeleted)
		{
			delete klock->lock;							//delete the lock instance related to this kernel lock
			delete klock;
			LockTable[i] = NULL;
			nextLockIndex = i;
			continue;
		}
	}
}

//Description: Validate input index of Condition
//Assumption: holding ConditionTableLock
bool ValidateConditionIndex(int index)
{
	if(index<0 || index>=ConditionTableSize)			//Invalid Index
	{
		printf("ERROR: Condition index [%d] out of bounds \n", index);
		return false;
	}

	KernelCondition * kcond = ConditionTable[index];

	if(kcond == NULL)									//Condition doesn't exist
	{
		printf("ERROR: requested Condition [%d] doesn't exist \n", index);
		return false;
	}

	if(kcond->processId != currentThread->processId )	//Condition doesn't belong to current process
	{
		printf("ERROR: requested Condition [%d] doesn't belong to current process \n", index);
		return false;
	}

	if(kcond->isDeleted)								//Condition has been deleted
	{
		printf("ERROR: requested Condition [%d] has been deleted \n", index);
		return false;
	}

	return true;										//Pass Validation
}

//Description: Clean Condition Table, remove deleted condition and update nextConditionIndex
//Assumption: holding ConditionTableLock
void CleanConditionTable()
{
	for(int i=ConditionTableSize - 1 ; i>=0 ; i--)
	{
		KernelCondition * kcond = ConditionTable[i];

		if(kcond == NULL)
		{
			nextConditionIndex = i;
			continue;
		}
		
		if(kcond->isDeleted)
		{
			delete kcond->condition;					//delete the condition instance related to this kernel condition
			delete kcond;
			ConditionTable[i] = NULL;
			nextConditionIndex = i;
			continue;
		}
	}
}

//---------------------------------------
//	Functionality Functions
//---------------------------------------
//return -1 if no found
int RequestMemoryPage()
{
	MemoryLock->Acquire();
	int pgn = MemoryMap->Find();		//If not found , return -1: Invalid flag

	DEBUG('2',"Physical Memory ALLOCATED page [%d] to process\n", pgn);

	MemoryLock->Release();
	return pgn;
}

void RecycleMemoryPage(int PhysicsPage)
{
	if( PhysicsPage<0 || PhysicsPage >= NumPhysPages)
	{
		printf("ERROR: Invalid Physics Page number %d to recycle \n", PhysicsPage);
		return;
	}

	MemoryLock->Acquire();
	MemoryMap->Clear(PhysicsPage);

	DEBUG('2',"Physical Memory RECYCLED page [%d] from process\n", PhysicsPage);

	MemoryLock->Release();
}

int RegisterLock(Lock * lock)
{
	int index = -1;

	if(lock == NULL)									//If Passed in pointer is NULL
	{
		printf("ERROR: Lock pointer passed in is NULL during register Lock \n");
		return index;
	}

	LockTableLock -> Acquire();
	
	//Add lock to Lock Table

	if(nextLockIndex == LockTableSize)					//If Lock Table is full
	{
		printf("ERROR: Lock Table is Full, Cannot add more Locks \n");
		LockTableLock -> Release();
		return index;
	}
	
	KernelLock * klock = new KernelLock();				//Build Kernel Lock struce

	klock -> lock = lock;								//Save Lock
	klock -> processId = currentThread->processId;		//Register self's process as kernel lock's owner
	klock -> isDeleted = false;
	klock -> toBeDeleted = false;
	//klock -> dependency = 0;

	LockTable[nextLockIndex] = klock;					//Add lock to lock table

	index = nextLockIndex;

	//Reserve Lock Table
	nextLockIndex ++;									//Update nextLockIndex to next NULL position in LockTable
	while( (LockTable[nextLockIndex]!=NULL) && (nextLockIndex < LockTableSize) )
		nextLockIndex++;

	if(nextLockIndex == LockTableSize)					//If nextLockIndex reached Table Bound
		CleanLockTable();								//Clean deleted structs and reset nextLockIndex;
	
	LockTableLock -> Release();

	DEBUG('2',"Lock Table: Lock '%s' is registered in Lock Table, Index [%d] \n", lock->getName(), index);

	return index;
}

void DeleteLock(int index)
{
	DEBUG('2',"Lock Table: delete Lock [%d]\n", index);
	
	LockTableLock -> Acquire();

	if(!ValidateLockIndex(index))						//Validate Lock Index
	{
		LockTableLock -> Release();
		return;
	}

	KernelLock * klock = LockTable[index];

	if(klock->lock->getWaitLength() <= 0)				//If NO thread is waiting for it, delete it NOW
	{
		klock->isDeleted = true;
		if(nextLockIndex == LockTableSize)				//If Lock table is currently Full
			CleanLockTable();
	}
	else
		klock->toBeDeleted = true;						//Else, Mark it as to be deleted

	LockTableLock -> Release();
}

void AcquireLock(int index)
{
	DEBUG('2',"Lock Table: Acquire Lock [%d] \n", index);
	
	LockTableLock -> Acquire();

	if(!ValidateLockIndex(index))						//Validate Lock Index
	{
		LockTableLock -> Release();
		return;
	}

	KernelLock * klock = LockTable[index];
	//klock -> dependency ++;								//update kernel lock info
	
	LockTableLock -> Release();							//Lock table operation over, release LockTableLock, IMPORTANT!!!

	klock -> lock -> Acquire();							//Acquire target lock, may sleep here
}

void ReleaseLock(int index)
{
	DEBUG('2',"Lock Table: Release Lock [%d] \n", index);
	
	LockTableLock -> Acquire();

	if(!ValidateLockIndex(index))						//Validate Lock Index
	{
		LockTableLock -> Release();
		return;
	}

	KernelLock * klock = LockTable[index];
	
	//klock -> dependency --;		//Pottential BUG!!!	//update kernel lock info
	
	klock -> lock -> Release();							//Release target lock

	//Continue Delete if possible
	if( (klock->toBeDeleted) && (klock->lock->getWaitLength() <= 0) )
	{
		klock->isDeleted = true;
		if(nextLockIndex == LockTableSize)				//If Lock table is currently Full
			CleanLockTable();
	}
	LockTableLock -> Release();						
}

int RegisterCondition(Condition * cond)
{
	int index = -1;

	if(cond == NULL)									//If Passed in pointer is NULL
	{
		printf("ERROR: Condition pointer passed in is NULL during register condition\n");
		return index;
	}

	ConditionTableLock -> Acquire();
	
	//Add condition to condition Table

	if(nextConditionIndex == ConditionTableSize)		//If Condition Table is full
	{
		printf("ERROR: Condition Table is Full , Cannot add more condition\n");
		ConditionTableLock -> Release();
		return index;
	}
	
	KernelCondition * kcond = new KernelCondition();	//Build Kernel Condition struce

	kcond -> condition = cond;							//Save Condition
	kcond -> processId = currentThread->processId;			//Register self's process as kernel Condition's owner
	kcond -> isDeleted = false;
	kcond -> toBeDeleted = false;

	ConditionTable[nextConditionIndex] = kcond;			//Add condition to table

	index = nextConditionIndex;

	//Reserve Condition Table
	nextConditionIndex ++;								//Update nextConditionIndex to next NULL position in ConditionTable
	while( (ConditionTable[nextConditionIndex]!=NULL) && (nextConditionIndex < ConditionTableSize) )
		nextConditionIndex++;

	if(nextConditionIndex == ConditionTableSize)		//If nextConditionIndex reached Table Bound
		CleanConditionTable();							//Clean deleted structs and reset nextConditionIndex;
	
	ConditionTableLock -> Release();

	DEBUG('2',"Condition Table: Condition '%s' is registered, Index [%d] \n", cond->getName(), index);

	return index;
}

void DeleteCondition(int index)
{
	DEBUG('2',"Condition Table: Delete Condition [%d] \n", index);
	
	ConditionTableLock -> Acquire();

	if(!ValidateConditionIndex(index))					//Validate Condition Index
	{
		ConditionTableLock -> Release();
		return;
	}

	KernelCondition * kcond = ConditionTable[index];

	if(kcond->condition->getWaitLength() <= 0)			//If NO thread is waiting for it, delete it NOW
	{
		kcond->isDeleted = true;
		if(nextConditionIndex == ConditionTableSize)	//If Condition table is currently Full
			CleanConditionTable();
	}
	else
		kcond->toBeDeleted = true;						//Else, Mark it as to be deleted

	ConditionTableLock -> Release();
}

void WaitCondition(int cond_index, int lock_index)
{
	DEBUG('2',"Condition Table: Wait Condition [%d] with Lock [%d] \n", cond_index, lock_index);
	
	ConditionTableLock -> Acquire();
	LockTableLock -> Acquire();

	if(! (ValidateConditionIndex(cond_index)&&ValidateLockIndex(lock_index)) )	//Validate Condition Index and lock index
	{
		LockTableLock -> Release();
		ConditionTableLock -> Release();
		return;
	}

	KernelCondition * kcond = ConditionTable[cond_index];
	Lock * lock = LockTable[lock_index]->lock;
	
	LockTableLock -> Release();							//Lock table operation over, release LockTableLock, IMPORTANT!!!
	ConditionTableLock -> Release();					//Condition table operation over, release ConditionTableLock, IMPORTANT!!!

	kcond -> condition -> Wait(lock);					//Wait target condition, may sleep here
}

//clarification:
//How to deal dependency between condition and locks?
//Needn't , user program should be responsible for that.
//We only count on number of threads waiting on condition

void SignalCondition(int cond_index, int lock_index)
{
	DEBUG('2',"Condition Table: Signal Condition [%d] with Lock [%d] \n", cond_index, lock_index);
	
	ConditionTableLock -> Acquire();
	LockTableLock -> Acquire();

	if(! (ValidateConditionIndex(cond_index)&&ValidateLockIndex(lock_index)) )	//Validate Condition Index and lock index
	{
		LockTableLock -> Release();
		ConditionTableLock -> Release();
		return;
	}

	KernelCondition * kcond = ConditionTable[cond_index];
	Lock * lock = LockTable[lock_index]->lock;

	kcond -> condition -> Signal(lock);					//Signal target condition

	//Continue Delete if possible
	if( (kcond->toBeDeleted) && (kcond->condition->getWaitLength() <= 0) )
	{
		kcond->isDeleted = true;
		if(nextConditionIndex == ConditionTableSize)	//If Condition table is currently Full
			CleanConditionTable();
	}
	LockTableLock -> Release();	
	ConditionTableLock -> Release();	
}

void BroadcastCondition(int cond_index, int lock_index)
{
	DEBUG('2',"Condition Table: Broadcast Condition [%d] with Lock [%d] \n", cond_index, lock_index);
	
	ConditionTableLock -> Acquire();
	LockTableLock -> Acquire();

	if(! (ValidateConditionIndex(cond_index)&&ValidateLockIndex(lock_index)) )	//Validate Condition Index and lock index
	{
		LockTableLock -> Release();
		ConditionTableLock -> Release();
		return;
	}

	KernelCondition * kcond = ConditionTable[cond_index];
	Lock * lock = LockTable[lock_index]->lock;

	kcond -> condition -> Broadcast(lock);					//Broadcast target condition

	//Continue Delete if possible
	if(kcond->toBeDeleted)
	{
		kcond->isDeleted = true;
		if(nextConditionIndex == ConditionTableSize)		//If Condition table is currently Full
			CleanConditionTable();
	}
	LockTableLock -> Release();	
	ConditionTableLock -> Release();	
}
//---------------------------------------
// Add end
//---------------------------------------

//---------------------------------------
//	Added in project2
//	Fork and Exec needed
//  Process Management
//  by OceanMaster
//---------------------------------------

//Description:
//	Forked thread run this method to load user function
//Assumption:
//	1)Current thread is the forked thread
//	2)Current thread has been assigned process id
//	3)Current thread has been assigned thread id !!!!! Especially In Fork System Call
//	4)Parent Process has inited Space

//	5)Current has NOT been assigned stack id
//	6) has NOT been assigned stack
//	7)Current has NOT been assigned thread id
//	8)Current has NOT been assigned space
void LoadUserFunction(int Vaddr)
{
	DEBUG('2',"Start Loading User Function from Address 0x%0x \n", Vaddr);

	KernelProcess* process = GetProcess(currentThread -> processId);
	
	if(process == NULL)
	{
		printf("FATAL ERROR: process doesn't exist \n", Vaddr);
		return;
	}

	AddrSpace* space = process -> space;

	if(space == NULL)
	{
		printf("FATAL ERROR: Addr Space is empty\n", Vaddr);
		return;
	}

	process -> addCurrentThread();
	//Assigned Stackid, processId, threadId

	currentThread -> space = space;
	//Assigned space

	//Set Registers
	for( int i=0; i < NumTotalRegs; i++ )
		machine -> WriteRegister(i,0);
	
	machine -> WriteRegister(PCReg,(unsigned int) Vaddr);

	machine -> WriteRegister(NextPCReg,((unsigned int) Vaddr) + 4);

	machine -> WriteRegister(StackReg, space -> GetStackVaddr(currentThread -> stackId));

	space -> RestoreState();

	//Jump to User Program
	machine -> Run();
}

//Description: 
//	Exec a new user program
//Assumption:
//	0)Potential Bug, Forker may quit even before loading program starts, and system may halt..
//		Solution: We may have to move "AddProcess" outside some day
//	Currently this is called by StartProcess, not forked, so it is OK
//	1)Current thread is a new thread
//	2)It should create Process first	ABANDONED
//	3)It should create AddrSpace and set it to proess	!MUST HERE!
//	4)It should add self to process
//	5)It should add attributes to self
//	4)This function never return

//Modified In Project3!!
//Addec Assumption:
//	currentThread should have been assigned ProcessId and ThreadId
//	before calling this function.

void LoadUserProgram(int executable)
{
	DEBUG('2',"Start Loading User Program \n");

	if(executable == NULL)						//check input
	{
		printf("FATAL ERROR: Failed to load user program because file pointer is NULL \n");
		return;

	}
	
	//1)Create Process
	//int processId = AddProcess();				//Moved to outside in Proj3

	KernelProcess * process = GetProcess(currentThread->processId);

	//2)Create Addr Space and set to process
	AddrSpace * space = new AddrSpace((OpenFile *)executable);

	process -> space = space;

	//3)Add self to Process
	
	//We can EXPLICITLY Assign an thread id to thread, but it is OK if we leave it -1, it will automatically be assigned later

	process -> addCurrentThread();

	currentThread -> space = space;

	//Project3, keep executable in addr space
	//delete (OpenFile *)executable;			// close file
	
	space -> InitRegisters();			//defaultl take first stack

	//IMPORTANT, This is the first thread in Stack
	//Therefore it should be allocated Stack Id 0,
	//Which corresponding to firset Stack,
	//Just as expected in InitRegisters. NO BUG		

	space -> RestoreState();

	machine -> Run();			//Jump to user program

}

//WARNING!!!!!
//This should be called JUST BEFORE halt system!
//Recycle all Resources!!
void CleanUpAll()
{
	for(int i=0;i<LockTableSize;i++)
	{
		if(LockTable[i]==NULL)
			continue;
		delete LockTable[i]->lock;
		delete LockTable[i];
		LockTable[i]=NULL;
	}
	
	for(int i=0;i<ConditionTableSize;i++)
	{
		if(ConditionTable[i]==NULL)
			continue;
		delete ConditionTable[i]->condition;
		delete ConditionTable[i];
		ConditionTable[i]=NULL;
	}
	
	for(int i=0;i<ProcessTableSize;i++)
	{
		if(ProcessTable[i]==NULL)
			continue;
		delete ProcessTable[i];
		ProcessTable[i]=NULL;
	}
}


//---------------------------------------
//	Process Utility
//---------------------------------------
int AddProcess()
{
	ProcessTableLock -> Acquire();
	if(nextProcessId >= ProcessTableSize)
	{
		printf("FATAL ERROR: Process Num Exceeds Process Table Size \n");
		ProcessTableLock -> Release();
		return -1;
	}	
	int id = nextProcessId++;
	processNum++;
	
	ProcessTable[id] = new KernelProcess(id);

	ProcessTableLock -> Release();

	DEBUG('2',"A new Process created and added to process table, id = [%d] \n", id);

	return id;
}

void DeleteProcess(int processId)
{
	DEBUG('2',"Try to delete Process [%d] \n", processId);
	
	bool isLast = false;	//is this last process?

	ProcessTableLock -> Acquire();
	if(processId < 0 || processId >= nextProcessId)
	{
		printf("ERROR: Process Id [%d] over bounds \n", processId);
		ProcessTableLock -> Release();
		return;
	}
	processNum--;
	isLast = (processNum==0);

	//Recycle Locks and Conditions allocated to this Process
	ConditionTableLock -> Acquire();
	LockTableLock -> Acquire();
	for(int i=0; i<LockTableSize; i++)			//For all Lock Table Entries
	{
		if(LockTable[i]==NULL)					//If Entry is NULL, skip
			continue;
		if(LockTable[i] -> processId != processId)	//If That lock doesn't belong to this process, skip
			continue;
		LockTable[i] -> isDeleted = true;
		if(nextLockIndex == LockTableSize)		//If Lock table is currently Full
			CleanLockTable();
	}
	for(int i=0; i<ConditionTableSize; i++)		//For all condition Table Entries
	{
		if(ConditionTable[i]==NULL)				//If Entry is NULL, skip
			continue;
		if(ConditionTable[i] -> processId != processId)	//If That Condition doesn't belong to this process, skip
			continue;
		ConditionTable[i] -> isDeleted = true;
		if(nextConditionIndex == ConditionTableSize)//If Condition table is currently Full
			CleanConditionTable();
	}
	LockTableLock -> Release();
	ConditionTableLock -> Release();
	
	//Delete Directly, memory pages automatically recycled

	//Modified in Project3
	//Bug fixed.
	//If it is last process, skip this step, cause CleanUpAll will clean up it.
	delete ProcessTable[processId];				//delete instance
	ProcessTable[processId] = NULL;
	
	DEBUG('2',"Process [%d] DELETE self from process table \n", processId);
	
	ProcessTableLock -> Release();

	//IF This is last process, do what?????????
	if(isLast)
	{	
		CleanUpAll();							//Recycle All Resources!
		//Project3: Needn't, cause it has been cleaned every proess quit.
		//And it causes segment fault. When you delete a null pointer.
		printf("All Process ended, Halt Nachos \n");
		interrupt -> Halt();
		//ASSERT(false);
	}

}

int GetProcessNum()
{
	return processNum;
}

KernelProcess* GetProcess(int processId)
{
	if(processId<0 || processId >= nextProcessId)
	{
		printf("ERROR: Get Process Failed due to invalid process Id [%d] \n",processId);
		return NULL;
	}
	return ProcessTable[processId];
}

//-----------------------
//KernelProcess class
//-----------------------

KernelProcess::KernelProcess(int id)
{
	ProcessId = id;
	//space = new AddrSpace();
	
	threadNum = 0;
	nextThreadId = 0;

	threadTableSize = 8;
	threadTableLock = new Lock("ThreadTableLock");
	threadTable = new Thread * [threadTableSize];
	
	for(int i=0;i<threadTableSize;i++)
		threadTable[i]=NULL;

}

KernelProcess::~KernelProcess()
{
	DEBUG('$',"Try to delete Addr Space\n");
	delete space;

	DEBUG('$',"Try to delete thread table, %s\n", currentThread -> getName());

	threadTableLock -> Acquire();

	//delete threads
	for(int i=0;i<threadTableSize;i++)
	{
		if(threadTable[i] == NULL)			//If it is NULL, skip
			continue;
		
		if(threadTable[i] == currentThread)	//!!If it is currentThread, skip
		{
			threadTable[i] = NULL;
			continue;
		}

		//dont't really delete thread..
		delete threadTable[i];				//delete that thread
		threadTable[i] = NULL;
	}
	
	threadTableLock -> Release();

	delete[] threadTable;
	
	delete threadTableLock;

	DEBUG('$',"Process Deleted\n");
}

//Important, this has to be explicitly called long before Fork LoadUserFunction,
//Of course long before addCurrentThread;
//Generally called in System Call Handler!!

//Assumption: Should Never be called TWICE for ONE thread!
int KernelProcess::AssignThreadId()
{
	threadTableLock -> Acquire();	//Protect with lock
	threadNum++;													//update thread num	
	int id = nextThreadId++;										//update nextThreadId

	if(id == threadTableSize)		//If threadTable is NOT big enough
	{

		Thread * * oldTable = threadTable;
		Thread * * newTable = new Thread*[threadTableSize*2];		//New thread table will double its current size
		
		for(int i=0;i<threadTableSize;i++)							//Copy data to new table
			newTable[i] = oldTable[i];
		
		for(int i=threadTableSize; i<threadTableSize*2; i++)		//Empty new table entity
			newTable[i] = NULL;
		
		threadTable = newTable;										//update thread table pointer
		delete[] oldTable;											//release abandoned space
	
		threadTableSize = threadTableSize*2;						//update table size
	}	

	threadTableLock -> Release();	//Protect with lock
	return id;
}


//It's OK to directly add a thread to a process without pre work
void KernelProcess::addCurrentThread()
{
	int id = currentThread -> threadId;

	if(id == -1)										//In case thread Id hasn't been assigned, assign it now!
	{
		id = AssignThreadId();
		currentThread -> threadId = id;
	}

	threadTableLock -> Acquire();						//Protect with lock
	threadTable[id] = currentThread;					//Add currentThread to new table
	threadTableLock -> Release();

	//update information in thread
	currentThread -> processId = ProcessId;				//this should be assigned before, here is just in case
	currentThread -> stackId = space -> AllocateStackId();		//allocate Stack id to thread in this process

	DEBUG('2',"Current Thread '%s' ADD self to process [%d] , thread id = [%d]; There are [%d] threads in this process now \n", currentThread -> getName(), ProcessId, id, threadNum);
}

void KernelProcess::deleteCurrentThread()
{
	deleteLock -> Acquire();

	threadNum--;										//update thread num
	
	threadTableLock -> Acquire();						//Protect with lock
	
	int id = currentThread -> threadId;	

	threadTable[id] = NULL;								//set table entity to NULL
	
	threadTableLock -> Release();
	
	//update information in thread
	//currentThread -> threadId = -1;	may be needed when recycle
	//currentThread -> processId = -1;	may be needed when recycle
	space -> RecycleStackId(currentThread -> stackId);	//Recycle Stack
	currentThread -> stackId = -1;

	int activeThread = GetActiveThreadNum();

	DEBUG('2',"Current Thread '%s' DELETE self from process [%d] , thread id = [%d]; There are [%d] threads in this process now, [%d] of them are ACTIVE \n", currentThread -> getName(), ProcessId, id, threadNum, activeThread);

	//Something More todo...

	if(activeThread <=0)								//If all threads are sleeping or ended, end this process
	{
		deleteLock -> Release();
		DeleteProcess(ProcessId);	
	}
	else
		deleteLock -> Release();
}

Thread * KernelProcess::GetThread(int threadId)
{
	threadTableLock -> Acquire();

	if( threadId<0 || threadId >= nextThreadId )
	{
		printf("ERROR : Invalid Thread Id [%d] \n", threadId);
		threadTableLock -> Release();
		return NULL;
	}
	
	Thread * thread = threadTable[threadId];

	threadTableLock -> Release();

	return thread;
}

int KernelProcess::GetThreadNum()
{
	return threadNum;
}

int KernelProcess::GetActiveThreadNum()
{
	int SleepingThreadNum = 0;

	//Count number of threads sleeping in Locks and Conditions

	ConditionTableLock -> Acquire();
	LockTableLock -> Acquire();
	
	for(int i=0; i<LockTableSize; i++)		//For all Lock Table Entries
	{
		if(LockTable[i]==NULL)				//If Entry is NULL, skip
			continue;
		if(LockTable[i] -> processId != ProcessId)	//If That lock doesn't belong to this process, skip
			continue;
		SleepingThreadNum += LockTable[i] -> lock -> getWaitLength();	//else , count on number of threads sleeping in it
	}
	
	for(int i=0; i<ConditionTableSize; i++)		//For all condition Table Entries
	{
		if(ConditionTable[i]==NULL)				//If Entry is NULL, skip
			continue;
		if(ConditionTable[i] -> processId != ProcessId)	//If That Condition doesn't belong to this process, skip
			continue;
		SleepingThreadNum += ConditionTable[i] -> condition -> getWaitLength();	//else , count on number of threads sleeping in it
	}
	
	LockTableLock -> Release();
	ConditionTableLock -> Release();
	
	return (threadNum - SleepingThreadNum);
}

//---------------------------------------
//				Add end
//---------------------------------------


#endif

#ifdef NETWORK
PostOffice *postOffice;
int serverNum;
int netname;
bool printLog;
#endif


// External definition, to allow us to take a pointer to this function
extern void Cleanup();


//----------------------------------------------------------------------
// TimerInterruptHandler
// 	Interrupt handler for the timer device.  The timer device is
//	set up to interrupt the CPU periodically (once every TimerTicks).
//	This routine is called each time there is a timer interrupt,
//	with interrupts disabled.
//
//	Note that instead of calling Yield() directly (which would
//	suspend the interrupt handler, not the interrupted thread
//	which is what we wanted to context switch), we set a flag
//	so that once the interrupt handler is done, it will appear as 
//	if the interrupted thread called Yield at the point it is 
//	was interrupted.
//
//	"dummy" is because every interrupt handler takes one argument,
//		whether it needs it or not.
//----------------------------------------------------------------------
static void
TimerInterruptHandler(int dummy)
{
    if (interrupt->getStatus() != IdleMode)
	interrupt->YieldOnReturn();
}

//----------------------------------------------------------------------
// Initialize
// 	Initialize Nachos global data structures.  Interpret command
//	line arguments in order to determine flags for the initialization.  
// 
//	"argc" is the number of command line arguments (including the name
//		of the command) -- ex: "nachos -d +" -> argc = 3 
//	"argv" is an array of strings, one for each command line argument
//		ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
//----------------------------------------------------------------------
void
Initialize(int argc, char **argv)
{
    int argCount;
    char* debugArgs = "";
    bool randomYield = FALSE;

	bool fifo = true;	//Project3 , FIFO?

#ifdef USER_PROGRAM
    bool debugUserProg = FALSE;	// single step user program
#endif
#ifdef FILESYS_NEEDED
    bool format = FALSE;	// format disk
#endif
#ifdef NETWORK
    double rely = 1;		// network reliability
    netname = 0;		// UNIX socket name
	serverNum = 1;
	printLog = false;
#endif
    
    for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount) {
	argCount = 1;
	if (!strcmp(*argv, "-d")) {
	    if (argc == 1)
		debugArgs = "+";	// turn on all debug flags
	    else {
	    	debugArgs = *(argv + 1);
	    	argCount = 2;
	    }
	} else if (!strcmp(*argv, "-rs")) {
	    ASSERT(argc > 1);
	    RandomInit(atoi(*(argv + 1)));	// initialize pseudo-random
						// number generator
	    randomYield = TRUE;
	    argCount = 2;
	}
#ifdef USE_TLB
//--------------Project3-----------------
//Select the algorithm used to evict
//memory pages
		if (!strcmp(*argv, "-P")) 
		{        
			if(argc == 1)
			{
				printf("Memory Replacement Policy Not Specified, use FIFO as default\n");
				argCount=1;
			}
			else
			{
				if(!strcmp(*(argv+1), "FIFO"))
				{
					printf("Memory Replacement Policy Selected : First In First Out\n");
					fifo = true;
					argCount=2;
				}
				else if(!strcmp(*(argv+1), "RAND"))
				{
					printf("Memory Replacement Policy Selected : Random\n");
					fifo = false;
					argCount=2;
				}
				else
				{
					printf("Memory Replacement Policy Not Specified, use FIFO as default\n");
					argCount=1;
				}
			}
        } 
//---------------------------------------
#endif //USE_TLB		
	
#ifdef USER_PROGRAM
	if (!strcmp(*argv, "-s"))
	    debugUserProg = TRUE;
#endif
#ifdef FILESYS_NEEDED
	if (!strcmp(*argv, "-f"))
	    format = TRUE;
#endif
#ifdef NETWORK
	if (!strcmp(*argv, "-l")) {
	    ASSERT(argc > 1);
	    rely = atof(*(argv + 1));
	    argCount = 2;
	} else if (!strcmp(*argv, "-m")) {
	    ASSERT(argc > 1);
	    netname = atoi(*(argv + 1));
	    argCount = 2;
	} else if(!strcmp(*argv, "-server")) {
		netname = 0;
	} else if ( !strcmp(*argv, "-SN") )
	{
		ASSERT(argc > 1);
	    serverNum = atoi(*(argv + 1));
	    argCount = 2;
	} else if ( !strcmp(*argv, "-log") )
	{
		printLog = true;
	}

#endif
    }

    DebugInit(debugArgs);			// initialize DEBUG messages
    stats = new Statistics();			// collect statistics
    interrupt = new Interrupt;			// start up interrupt handling
    scheduler = new Scheduler();		// initialize the ready queue
    if (randomYield)				// start the timer (if needed)
	timer = new Timer(TimerInterruptHandler, 0, randomYield);

    threadToBeDestroyed = NULL;

    // We didn't explicitly allocate the current thread we are running in.
    // But if it ever tries to give up the CPU, we better have a Thread
    // object to save its state. 
    currentThread = new Thread("main");		
    currentThread->setStatus(RUNNING);

    interrupt->Enable();
    CallOnUserAbort(Cleanup);			// if user hits ctl-C

#ifdef USER_PROGRAM
    machine = new Machine(debugUserProg);	// this must come first

//----------------------------------
// Project3
// Initialize MMU Management
//----------------------------------
#ifdef USE_TLB
initMMU();
useFIFO = fifo;
#endif //USE_TLB    


//--------------------------------------------
// Added in Project2
// Initialization
//--------------------------------------------

	deleteLock = new Lock("DeleteLock");

	ThreadIndex = 0;

    ProcessTable = new KernelProcess * [ProcessTableSize];
	for(int i=0;i<ProcessTableSize;i++)
		ProcessTable[i]=NULL;

    ProcessTableLock = new Lock("ProcessTableLock");
    nextProcessId = 0;
    processNum = 0;
	
    MemoryMap = new BitMap(NumPhysPages);
    MemoryLock = new Lock("MemoryLock");
		
    LockTable = new KernelLock*[LockTableSize];

    for(int i=0;i<LockTableSize;i++)
		LockTable[i]= NULL;
	
    LockTableLock = new Lock("LockTableLock");

    nextLockIndex = 0;
    
    ConditionTable = new KernelCondition*[ConditionTableSize];

    for(int i=0;i<ConditionTableSize;i++)
		ConditionTable[i]= NULL;
	
    ConditionTableLock = new Lock("ConditionTableLock");

    nextConditionIndex = 0;


//--------------------------------------------
// Add end									*/
//--------------------------------------------
#endif

#ifdef FILESYS
    synchDisk = new SynchDisk("DISK");
#endif

#ifdef FILESYS_NEEDED
    fileSystem = new FileSystem(format);
#endif

#ifdef NETWORK
	printf("Create Nacho instance with MachineID: %d\n",netname);
    postOffice = new PostOffice(netname, rely, 100);
#endif
}

//----------------------------------------------------------------------
// Cleanup
// 	Nachos is halting.  De-allocate global data structures.
//----------------------------------------------------------------------
void
Cleanup()
{
    printf("\nCleaning up...\n");
#ifdef NETWORK
    delete postOffice;
	//printf("PostOffice Cleaned up\n");
#endif


//----------------------------------
// Project3
// Finalize MMU Management
//----------------------------------
#ifdef USE_TLB
	finalizeMMU();
	//printf("MMU Cleaned up\n");
#endif //USE_TLB    



#ifdef USER_PROGRAM
    delete machine;

//---------------------------
//  Clean up for Project2
//	by OceanMaster
//---------------------------

	delete ProcessTableLock;
	delete MemoryMap;
	delete MemoryLock;

	delete[] ProcessTable; 
	
	delete[] LockTable;

	delete LockTableLock;

	delete[] ConditionTable;

	delete ConditionTableLock;

	delete deleteLock;

	//printf("User prog Cleaned up\n");

//---------------------------

#endif

#ifdef FILESYS_NEEDED
    delete fileSystem;
#endif

#ifdef FILESYS
    delete synchDisk;
#endif
    
    delete timer;
    delete scheduler;
    delete interrupt;
    
    Exit(0);
}

